home *** CD-ROM | disk | FTP | other *** search
- /* rnews.c - simple rf?news/r[cf]smtp for MiNT + taylor uuxqt.
- * uses pipes instead of tempfiles where possible and tries to handle
- * error conditions gracefully.
- *
- * send bugs + comments to: Juergen Lock <nox@jelal.north.de>
- *
- */
-
- #define SYS_RNEWS "/usr/lib/nn/bin/rnews.tos", "rnews", "-z", "\\dev\\stdin"
- #define SYS_RSMTP "/usr/lib/nn/bin/rsmtp.ttp", "rsmtp"
- #define DEBUG_DIR "/usr/spool/uucp/.Preserve"
-
- /* command (execl args) to pipe uncompressed incoming news into */
- #ifndef SYS_RNEWS
- #define SYS_RNEWS "/usr/bin/rnews.ttp", "rnews", "-z", "\\dev\\stdin"
- #endif
-
- /* same for incoming smtp batches */
- #ifndef SYS_RSMTP
- #define SYS_RSMTP "/usr/bin/rsmtp.ttp", "rsmtp"
- #endif
-
- /* make this 0 if your rnews doesn't hate pipes... */
- #define RNEWS_FILE 1
-
- /* directory to save input of failed requests to. define this if you
- want to examine the files yourself because uuxqt discards failed jobs
- that don't want to be retried later (EX_TEMPFAIL). */
- /* #define DEBUG_DIR "/usr/spool/uucp/.Preserve" /* */
-
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <ioctl.h>
- #include <process.h>
- #include <unistd.h>
- #include <errno.h>
-
- /* make sure we have EX_TEMPFAIL */
- #ifndef EX_TEMPFAIL
- #define EX_TEMPFAIL 75
- #endif
-
- /* default argv[0] value to use when we're called with argv[0] == "".
- taylor uuxqt never does that (like any program linked with the MiNT
- libraries) but it costs us nothing and maybe its useful some day. */
- #ifdef RNEWS
- char *argv0 = "rnews";
- #endif
- #ifdef RFNEWS
- char *argv0 = "rfnews";
- #endif
- #ifdef RSMTP
- char *argv0 = "rsmtp";
- #endif
- #ifdef RCSMTP
- char *argv0 = "rcsmtp";
- #endif
- #ifdef RFSMTP
- char *argv0 = "rfsmtp";
- #endif
-
- #ifdef DEBUG_DIR
- int savestdin;
- #endif
-
- /* pid of the exec'd compress or freeze, if any */
- int cpid;
- /* read end of the pipe connected to its stderr */
- int cerrfd;
- /* exit code */
- int cret;
- #if RNEWS_FILE
- char *tmpf = (char *) NULL;
- #endif
-
- /* pid of rnews/rsmtp */
- int rpid;
- /* and its exit code */
- int ret;
-
- /* wait for a child. always check the pid because we usually don't
- know wich one dies first. */
- void cwait ()
- {
- int status, pid;
-
- pid = wait (&status);
- if (pid == cpid) {
- cret = status >> 8;
- cpid = 0;
- } else if (pid == rpid) {
- ret = status >> 8;
- rpid = 0;
- }
- }
-
- /* print error message, cleanup and exit. if DEBUG_DIR is defined and
- the exit code isn't EX_TEMPFAIL (meaning uuxqt can retry the job
- later) then copy our input. */
- void fail (ex, msg)
- int ex;
- char *msg;
- {
- if (msg)
- fprintf (stderr, "%s: %s\n", argv0, msg);
- if (cpid > 0) {
- /* close the pipes */
- close (0);
- close (cerrfd);
- cwait ();
- }
- #ifdef DEBUG_DIR
- if (ex != EX_TEMPFAIL) {
- char copyf[sizeof DEBUG_DIR + 20], *buf = malloc (0x4000);
- int fd, count;
-
- /* just copy it to DEBUG_DIR/rnews.123 etc. if the copy
- fails too return EX_TEMPFAIL because thats the only
- thing we can do so that the file doesn't get lost.
-
- FIXME: should probably write a log file or something... */
-
- lseek (savestdin, 0, SEEK_SET);
- sprintf (copyf, "%s/%s.XXX", DEBUG_DIR, argv0);
- if (!buf || !mktemp (copyf) ||
- (fd = creat (copyf, 0600)) < 0)
- exit (EX_TEMPFAIL);
- while ((count = read (savestdin, buf, 0x4000)) > 0) {
- if (write (fd, buf, count) != count) {
- close (fd);
- remove (copyf);
- exit (EX_TEMPFAIL);
- }
- }
- if (close (fd) < 0 || count < 0) {
- remove (copyf);
- exit (EX_TEMPFAIL);
- }
- }
- #endif
- exit (ex);
- }
-
- int main (argc, argv)
- int argc;
- char **argv;
- {
- char *p;
- int news = 0, uncompressed = 1, freeze = 0;
-
- /* what are we? */
- if (*argv && **argv) {
- argv0 = *argv;
- /* extract the basename, without .ttp etc */
- if (*argv0 && argv0[1] == ':')
- argv0 += 2;
- if (p = strrchr (argv0, '/'))
- argv0 = p + 1;
- if (p = strrchr (argv0, '\\'))
- argv0 = p + 1;
- if (p = strchr (argv0, '.'))
- *p = 0;
- }
- if (!strcmp (argv0, "rnews"))
- news = 1;
- else if (!strcmp (argv0, "rfnews") || !strcmp (argv0, "frnews"))
- news = 1, uncompressed = 0, freeze = 1;
- else if (!strcmp (argv0, "rsmtp"))
- ;
- else if (!strcmp (argv0, "rcsmtp") || !strcmp (argv0, "crsmtp"))
- uncompressed = 0;
- else if (!strcmp (argv0, "rfsmtp") || !strcmp (argv0, "frsmtp"))
- uncompressed = 0, freeze = 1;
- else {
- fprintf (stderr, "%s: what am i?? :-) unknown argv[0] value.\n", argv0);
- /* input gets not saved here! */
- exit (1);
- }
- if (argc != 1) {
- fprintf (stderr, "%s: hmm... this is ment to be called by uuxqt only, and gets no args.\n", argv0);
- exit (1);
- }
-
- /* if we are rnews we have to look at the input to see how to
- uncompress. */
- if (news && uncompressed) {
- char buf[100];
- long start;
-
- if (read (0, buf, sizeof buf) < sizeof buf - 1)
- fail (1, "input too short, can hardly be a news batch...");
-
- buf[sizeof buf - 1] = 0;
- if (*buf != '#' || buf[1] != '!' ||
- !(p = strchr (buf + 2, '\n')) ||
- ((start = p - buf + 1), (*p = 0),
- (p = buf + 2 + strspn (buf + 2, " \t"))) >= buf + start ||
- (!(uncompressed = !strncmp (p, "rnews", sizeof "rnews" -1)) &&
- !(freeze = !strcmp (p, "funbatch")) &&
- strcmp (p, "cunbatch") != 0))
-
- fail (1, "unknown format, don't know how to uncompress.");
-
- /* now seek back to where the fun begins. */
- if (uncompressed)
- /* `#! rnews' is already part of the batch */
- start = 0;
- /* ...else skip the `#! [cf]unbatch'. */
- if (lseek (0, start, SEEK_SET) != start)
- fail (1, "huh!? can't lseek back on input!");
- }
-
- /* if input is compressed set up a pipe to uncompress. */
- if (!uncompressed) {
- int pipefd[2], perrfd[2];
- char *compress = freeze ? "freeze" : "compress";
-
- if (pipe (pipefd) < 0 || pipe (perrfd) < 0 || (cpid = fork()) < 0)
- fail (EX_TEMPFAIL, "pipe or fork failed. (not enough memory?)");
-
- if (!cpid) {
- /* child */
-
- /* close read ends of the pipes */
- close (pipefd[0]);
- close (perrfd[0]);
- /* connect compress/freeze's stdout and stderr to
- the pipes... */
- if (dup2 (pipefd[1], 1) < 0 || dup2 (perrfd[1], 2) < 0)
- exit (EX_TEMPFAIL);
- close (pipefd[1]);
- close (perrfd[1]);
- /* ...and exec it. */
- execlp (compress, compress, "-d", (char *) NULL);
- /* exec failed, probably also temporary. */
- perror (compress);
- exit (EX_TEMPFAIL);
- }
- /* parent */
-
- /* close write ends */
- close (pipefd[1]);
- close (perrfd[1]);
-
- /* save stderr */
- cerrfd = perrfd[0];
- #ifdef DEBUG_DIR
- /* save stdin in case we have to copy it... */
- savestdin = dup (0);
- #endif
- /* connect stdin to the uncompressed output */
- if (dup2 (pipefd[0], 0) < 0) {
- close (pipefd[0]);
- fail (EX_TEMPFAIL, strerror (errno));
- }
- close (pipefd[0]);
- }
-
- #if RNEWS_FILE
- /* DI rnews wants a file as input so we make one. (poor /tmp disk..) */
-
- if (news && !uncompressed) {
- int count, fd;
- char *buf = malloc (0x4000);
-
- if (!buf || !(tmpf = tmpnam((char *) NULL)) ||
- (fd = creat (tmpf, 0600)) < 0)
- fail (EX_TEMPFAIL, strerror (errno));
- while ((count = read (0, buf, 0x4000)) > 0) {
- if (write (fd, buf, count) != count) {
- int err = errno;
-
- close (fd);
- unlink (tmpf);
- free (tmpf);
- free (buf);
- fail (EX_TEMPFAIL, strerror (err));
- }
- }
- free (buf);
- if (count < 0 || lseek (fd, 0l, SEEK_SET) ||
- dup2 (fd, 0) < 0) {
- int err = errno;
-
- close (fd);
- unlink (tmpf);
- free (tmpf);
- free (buf);
- fail (EX_TEMPFAIL, strerror (err));
- }
- close (fd);
- free (buf);
- }
- #endif /* RNEWS_FILE */
-
- /* stdin now is the uncompressed batch either as file or pipe */
- if (news)
- rpid = spawnl (P_NOWAIT, SYS_RNEWS, (char *) NULL);
- else
- rpid = spawnl (P_NOWAIT, SYS_RSMTP, (char *) NULL);
-
- if (rpid == -1)
- /* if the spawn itself failed assume it can be retried. */
- fail (EX_TEMPFAIL, strerror (errno));
-
- if (uncompressed)
- /* wait for the child */
- cwait ();
- else {
- char errmsg [0x200];
- long clen;
-
- /* compress and freeze don't return special error codes
- for `not enough memory', so we read its stderr. */
- if (ioctl (cerrfd, FIONREAD, &clen) < 0) {
- int err = errno;
-
- cwait ();
- cwait ();
- #if RNEWS_FILE
- if (tmpf) {
- /* kill the tempfile */
- close (0);
- unlink (tmpf);
- free (tmpf);
- }
- #endif
- fail (EX_TEMPFAIL, strerror (err));
- }
- if (clen > 0)
- clen = read (cerrfd, errmsg, sizeof errmsg - 1);
- else
- clen = 0;
- errmsg[clen] = 0;
-
- /* close the pipes */
- close (0);
- close (cerrfd);
-
- /* wait for both childs */
- cwait();
- cwait();
-
- #if RNEWS_FILE
- if (tmpf) {
- /* kill the tempfile */
- unlink (tmpf);
- free (tmpf);
- }
- #endif
- if (cret) {
- /* kill trailing \n in errmsg because fail will
- append one itself. */
- if (clen && errmsg[clen - 1] == '\n')
- errmsg[clen - 1] = 0;
- if (strstr (errmsg, "nsufficient memory") ||
- strstr (errmsg, "eap overflow") ||
- strstr (errmsg, "ut of memory"))
- cret = EX_TEMPFAIL;
- if (!*errmsg)
- fail (cret, (char *) NULL);
- fail (cret, errmsg);
- }
- }
-
- if (!ret || (ret = EX_TEMPFAIL))
- exit(ret);
-
- fail (ret, (char *) NULL);
- }
-